﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using HB.HttpServer.Fun;
using HB.HttpServer.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using static System.Net.Mime.MediaTypeNames;
using static System.Net.WebRequestMethods;

namespace HB.HttpServer
{
    public class HttpServer : IDisposable
    {
        HttpListener listener;
        Router router;
        public bool IsSupportOPTIONS = false;
        public bool IsEnabledDirectoryBrowse = false;
        IAuthenticationFilter authenticationFilter = null;
        IActionFilter actionFilter = null;
        IFileFilter fileFilter = null;
        IExceptionFilter exceptionFilter = null;
        /// <summary>
        /// 实例化httpserver
        /// </summary>
        public HttpServer()
        {
            listener = new HttpListener();
            router = new Router();
        }
        void DoAttribute(Method md, params object[] attrs)
        {
            foreach (var attr in attrs)
            {
                Type t = attr.GetType();
                //【叠加特性】
                if (t == typeof(HttpGET) && !md.SupportHttpMethod.Contains("GET"))
                {
                    md.SupportHttpMethod.Add("GET"); continue;
                }
                //【叠加特性】
                if (t == typeof(HttpPOST) && !md.SupportHttpMethod.Contains("POST"))
                {
                    md.SupportHttpMethod.Add("POST"); continue;
                }
                //【叠加特性】
                if (t == typeof(HttpOPTIONS) && !md.SupportHttpMethod.Contains("OPTIONS"))
                {
                    md.SupportHttpMethod.Add("OPTIONS"); continue;
                }
                //【覆盖特性】
                if (t == typeof(HttpLicense))
                {
                    md.License = attr as HttpLicense; continue;
                }
                //【覆盖特性】
                if (t == typeof(HttpFilterIgnore))
                {
                    md.IsFilterIgnore = true; continue;
                }
                //【覆盖特性】
                if (t == typeof(HttpFrequency))
                {
                    HttpFrequency af = attr as HttpFrequency;
                    md.FilterFrequency = af.Frequency;
                    md.LastestAcceptTime = DateTime.Now;
                    switch (af.Filtertype)
                    {
                        case HttpFrequency.FilterType.ForIp:
                            md.DicIpAcceptTime = new Dictionary<string, DateTime>();
                            md.IsFilterIp = true;
                            md.IsFilterPublic = false;
                            break;
                        case HttpFrequency.FilterType.ForPublic:
                            md.IsFilterPublic = true;
                            md.IsFilterIp = false;
                            md.DicIpAcceptTime = null;
                            break;
                    }
                    continue;
                }
                //【覆盖特性】
                if (t == typeof(HttpMaxContentLength))
                {
                    var maxlen = attr as HttpMaxContentLength;
                    md.IsLimitContentLength = maxlen.NeedLimit;
                    md.MaxContentLength = maxlen.Length;
                    continue;
                }
                //【覆盖特性】
                if (t == typeof(HttpRouter))
                {
                    var tempRouter = attr as HttpRouter;
                    md.Router = tempRouter.routerFormat;
                }
            }
        }
        /// <summary>
        /// using the reflection to get the mathod,the default router is "[host]/api/[classname]/[methodname]"
        /// </summary>
        /// <param name="T"></param>
        public void AddController<TT>() where TT : ApiController, new()
        {
            Type T = typeof(TT);
            TT instance = new TT();
            var methods = T.GetMethods().Where(zz => zz.DeclaringType == T);//获取此类中定义的方法，去掉继承的方法                       
            MethodInfo main = T.BaseType.GetMethod("Exec", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
            if (main == null) { throw new Exception("请继承ApiContronller"); }
            //控制器级别通用特性
            var controllerAttrs = T.GetCustomAttributes(false);
            foreach (var method in methods)
            {
                if (method.IsPublic)
                {
                    Method md = new Method() { Methodinfo = method, Main = main, ControllerInstance = instance };
                    DoAttribute(md, controllerAttrs);//注入控制器级别特性
                    var methodAttrs = method.GetCustomAttributes(true);
                    DoAttribute(md, methodAttrs);//注入方法级特性
                    //设置实际路由
                    md.Router = md.Router.Replace("[controller]", T.Name).Replace("[action]", method.Name);
                    router.Add(md.Router, md);
                }
            }
        }
        /// <summary>
        /// 启动服务
        /// </summary>
        /// <param name="portlist"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool Start(params ushort[] portlist)
        {
            if (portlist.Count() < 1)
            {
                throw new Exception("无效端口号");
            }
            bool result = false;
            foreach (var p in portlist.Distinct())
            {
                listener.Prefixes.Add($"http://localhost:{p.ToString()}/");
                listener.Prefixes.Add($"http://127.0.0.1:{p.ToString()}/");
            }
            listener.Start();
            var tr = listener.BeginGetContext(GetContextCallBack, listener);
            result = true;
            return result;
        }
        /// <summary>
        /// 启动服务
        /// </summary>
        /// <param name="portlist"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool StartAllHost(params ushort[] portlist)
        {
            if (portlist.Count() < 1)
            {
                throw new Exception("无效端口号");
            }
            bool result = false;
            foreach (var p in portlist.Distinct())
            {
                listener.Prefixes.Add($"http://*:{p.ToString()}/");
            }
            listener.Start();
            var tr = listener.BeginGetContext(GetContextCallBack, listener);
            result = true;
            return result;
        }
        void GetContextCallBack(IAsyncResult ar)
        {
            var sSocket = ar.AsyncState as HttpListener;
            try
            {
                sSocket.BeginGetContext(new AsyncCallback(GetContextCallBack), sSocket);
                HttpListenerContext context = sSocket.EndGetContext(ar);

                if (context == null) { return; }
                var request = context.Request;
                var response = context.Response;
                var method = router.Get_MenberInfo(request.Url.AbsolutePath);
                ActionExecutingData AedData = new ActionExecutingData() { HttpContex = context, Method = method };
                if (method != null)
                {
                    string methodname = request.HttpMethod.ToUpper();
                    if (method.SupportHttpMethod.Contains(methodname))
                    {
                        if (ApiController.DicContent == null) { ApiController.DicContent = new Dictionary<string, ActionExecutingData>(); }
                        string key = Guid.NewGuid().ToString();
                        //跨域处理
                        if (AedData.CanContinue)
                        {
                            if (IsSupportOPTIONS)
                            {
                                response.Headers.Add("Access-Control-Allow-Origin", "*");
                                response.Headers.Add("Access-Control-Allow-Methods", string.Join(",", method.SupportHttpMethod));
                                response.Headers.Add("Access-Control-Allow-Headers", "x-requested-with,content-type,Authorization");
                            }
                            if (methodname == "OPTIONS")//通过预访问
                            {
                                AedData.ReturnCode(HttpStatusCode.OK);
                            }
                        }
                        //内容长度限制
                        if (AedData.CanContinue && method.IsLimitContentLength)
                        {
                            if (request.ContentLength64 > method.MaxContentLength)
                            {
                                AedData.ReturnCode(HttpStatusCode.BadRequest);
                            }
                        }
                        //全局频率过滤
                        if (AedData.CanContinue)
                        {
                            if (method.IsFilterPublic)
                            {
                                if ((DateTime.Now - method.LastestAcceptTime).TotalSeconds < method.FilterFrequency)//超频访问
                                {
                                    AedData.ReturnCode(HttpStatusCode.Forbidden);
                                }
                                method.LastestAcceptTime = DateTime.Now;
                            }
                        }
                        //IP频率限制
                        if (AedData.CanContinue)
                        {
                            if (method.IsFilterIp)
                            {
                                var ip = request.RemoteEndPoint.Address.ToString();
                                if (method.DicIpAcceptTime.ContainsKey(ip))
                                {
                                    if ((DateTime.Now - method.DicIpAcceptTime[ip]).TotalSeconds < method.FilterFrequency)
                                    {
                                        AedData.ReturnCode(HttpStatusCode.Forbidden);
                                    }
                                    else
                                    {
                                        method.DicIpAcceptTime[ip] = DateTime.Now;
                                    }
                                }
                                else
                                {
                                    method.DicIpAcceptTime.Add(ip, DateTime.Now);
                                }
                            }
                        }
                        if (AedData.CanContinue)
                        {
                            //记录当前请求体                       
                            ApiController.DicContent[key] = AedData;
                        }
                        //建立过程文件数据
                        if (AedData.CanContinue && request.HasEntityBody)
                        {
                            byte[] data = new byte[request.ContentLength64];
                            request.InputStream.Read(data, 0, data.Length);
                            AedData.RequestBodyContent = data;
                        }
                        //是否经过过滤器                 
                        if (AedData.CanContinue && !method.IsFilterIgnore)
                        {
                            //Auth过滤
                            authenticationFilter?.OnAuthentication(AedData);
                            if (AedData.CanContinue)
                            {
                                //预执行
                                actionFilter?.OnActionExecuting(AedData);
                            }

                        }
                        //取参数，执行主方法
                        if (AedData.CanContinue)
                        {
                            List<dynamic> LsParamaterMain = new List<dynamic>();
                            List<dynamic> LsParamater = new List<dynamic>();
                            LsParamaterMain.Add(context);
                            LsParamaterMain.Add(method);

                            var param = method.Methodinfo.GetParameters();
                            try
                            {
                                foreach (var par in param)
                                {
                                    //含有body标记或者是class
                                    var IsFromBody = par.GetCustomAttributes(false).FirstOrDefault(zz => zz is HttpFromBody) as HttpFromBody != null;
                                    var IsClass = par.ParameterType != typeof(string) && par.ParameterType.GetProperties().Length > 0;
                                    if (IsFromBody || IsClass)
                                    {
                                        if (request.HasEntityBody)
                                        {
                                            StreamReader reader = new StreamReader(new MemoryStream(AedData.RequestBodyContent));
                                            string json = reader.ReadToEnd();
                                            reader.Close();
                                            Type BodyType = par.ParameterType;
                                            var obj = JsonConvert.DeserializeObject(json, BodyType);
                                            LsParamater.Add(obj);
                                        }
                                        else
                                        {
                                            if (par.DefaultValue != null)
                                            {
                                                LsParamater.Add(par.DefaultValue);
                                            }
                                            else
                                            {
                                                AedData.ReturnCode(HttpStatusCode.BadRequest);
                                                break;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        var temp = request.QueryString[par.Name];
                                        if (temp != null)
                                        {
                                            if (par.ParameterType == typeof(int)) { LsParamater.Add(int.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(long)) { LsParamater.Add(long.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(string)) { LsParamater.Add(temp); continue; }
                                            if (par.ParameterType == typeof(Guid)) { LsParamater.Add(Guid.Parse(temp)); continue; }
                                            //use less
                                            if (par.ParameterType == typeof(short)) { LsParamater.Add(short.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(ushort)) { LsParamater.Add(ushort.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(ulong)) { LsParamater.Add(ulong.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(uint)) { LsParamater.Add(uint.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(float)) { LsParamater.Add(float.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(double)) { LsParamater.Add(double.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(sbyte)) { LsParamater.Add(sbyte.Parse(temp)); continue; }
                                            if (par.ParameterType == typeof(byte)) { LsParamater.Add(byte.Parse(temp)); continue; }
                                        }
                                        else
                                        {
                                            if (par.DefaultValue != null)
                                            {
                                                LsParamater.Add(par.DefaultValue);
                                            }
                                            else
                                            {
                                                AedData.ReturnCode(HttpStatusCode.BadRequest);
                                                break;
                                            }
                                        }
                                    }
                                }
                                LsParamaterMain.Add(LsParamater);
                            }

                            catch (Exception ex)
                            {
                                AedData.ReturnCode(HttpStatusCode.BadRequest, ex.Message);
                            }
                            if (AedData.CanContinue)
                            {
                                try
                                {
                                    var result = method.Main.Invoke(null, LsParamaterMain.ToArray());
                                    if (result is Task)
                                    {
                                        var type = result.GetType();
                                        var p = type.GetProperty("Result");
                                        AedData.ReturnJson(JsonConvert.SerializeObject(p.GetValue(result, null)));
                                    }
                                    else
                                    {
                                        AedData.ReturnJson(JsonConvert.SerializeObject(result));
                                    }
                                }
                                catch (Exception ex)
                                {
                                    AedData.ReturnCode(HttpStatusCode.InternalServerError);
                                    exceptionFilter?.OnException(ex); //异常过滤器                          
                                }
                                finally
                                {
                                    LsParamater?.Clear();
                                    LsParamater = null;
                                    LsParamaterMain?.Clear();
                                    LsParamaterMain = null;
                                }
                            }
                        }
                        actionFilter?.OnActionExecuted(AedData);   //执行后过滤器
                        ApiController.Clear(key);
                    }
                    else//不允许的方法 
                    {
                        AedData.ReturnCode(HttpStatusCode.MethodNotAllowed);
                    }
                }
                else
                {//文件访问
                    bool accept = false;
                    if (FileServerDir != null)
                    {
                        var path = request.Url.AbsolutePath;
                        path = System.Web.HttpUtility.UrlDecode(path, Encoding.UTF8);
                        var file = Path.Combine(FileServerDir, path.Substring(1, path.Length - 1));
                        if (fileFilter?.OnFileRead(file) ?? true)
                        {
                            if (System.IO.File.Exists(file))
                            {
                                accept = true;
                                AedData.ReturnFile(file);
                            }
                            else
                            {
                                if (IsEnabledDirectoryBrowse)
                                {
                                    if (Directory.Exists(file))
                                    {
                                        accept = true;
                                        HtmlHelper html = new HtmlHelper();
                                        html.AddHeading(HtmlHelper.Headings.H3, "目录浏览", HtmlHelper.Aligns.Center);
                                        html.AddHeading(HtmlHelper.Headings.H6, $"当前目录：{path}", HtmlHelper.Aligns.Left);
                                        var files = Directory.GetFileSystemEntries(file);
                                        foreach (var f in files)
                                        {
                                            string showName = (Directory.Exists(f) ? "/" : "") + Path.GetFileName(f);
                                            html.AddLink(showName, f.Replace(FileServerDir, ""));
                                            html.AddNewLine();
                                        }
                                        AedData.ReturnHtml(html.Build());
                                    }
                                }

                            }
                        }
                    }
                    if (!accept)
                    {
                        //404未找到 或被拦截
                        AedData.ReturnCode(HttpStatusCode.NotFound);
                    }
                }
                response.Close();
            }
            catch (Exception ex)
            {
                return;
            }
        }
        /// <summary>
        /// 获取所有控制器方法
        /// </summary>
        /// <returns></returns>
        public string[] GetSupportURL(string key = null)
        {
            return router.GetSupportURL(key);
        }
        /// <summary>
        /// 启用文件服务器
        /// </summary>
        /// <param name="dir"></param>
        /// <exception cref="Exception"></exception>
        public void UsingFileServer(string dir, bool isEnabledDirectoryBrowse = false)
        {
            if (Directory.Exists(dir))
            {
                FileServerDir = dir;
                IsEnabledDirectoryBrowse = isEnabledDirectoryBrowse;
            }
            else
            {
                throw new Exception("Dir No Found");
            }
        }
        /// <summary>
        /// 使用过滤器 继承接口后实现（IAuthenticationFilter，IActionFilter，IExceptionFilter，IFileFilter）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <exception cref="Exception"></exception>
        public void UsingFilter<T>() where T : class, new()
        {
            var type = typeof(T);
            var instance = new T();
            if (instance is IAuthenticationFilter) { authenticationFilter = instance as IAuthenticationFilter; return; }
            if (instance is IActionFilter) { actionFilter = instance as IActionFilter; return; }
            if (instance is IExceptionFilter) { exceptionFilter = instance as IExceptionFilter; return; }
            if (instance is IFileFilter) { fileFilter = instance as IFileFilter; return; }
            throw new Exception("No Support Filter");
        }
        string FileServerDir { get; set; }
        public void SetResponseCamelCase(EnumCamelCase cases)
        {
            IContractResolver resolver = null;
            switch (cases)
            {
                case EnumCamelCase.Default:
                    resolver = new DefaultContractResolver();
                    break;
                case EnumCamelCase.SmallCamelCase:
                    resolver = new CamelCasePropertyNamesContractResolver();
                    break;
            }
            Jsetting = new JsonSerializerSettings()
            {
                ContractResolver = resolver
            };
        }
        static JsonSerializerSettings Jsetting { get; set; } = null;
        /// <summary>
        /// Close Server And Dispose
        /// </summary>
        public void Dispose()
        {
            listener.Prefixes.Clear();
            if (listener.IsListening) { listener.Stop(); }
            listener.Close();
        }
    }
}
